#include <cstdio>
#include <random>
#include <string>

#include "buffer/buffer_pool_manager.h"
#include "catalog/catalog.h"
#include "catalog/table_generator.h"
#include "concurrency/transaction_manager.h"
#include "execution/executor_context.h"
#include "json/json.h"
#include "storage/disk/disk_manager.h"
#include "type/value_factory.h"
#define MAX_LINE 1024             //每行最大字节数
#define THIS_BUFFER_POOL_SIZE 10  //测试BufferPool大小

static constexpr uint32_t MAX_VARCHAR_SIZE = 128;
static std::string db_name = "lab1";
static std::string table_name = "test_table";
static std::string db_file_name = "lab1.db";
static std::string log_file_name = "lab1.log";
static std::string absolute_path = "/home/aaron/Course-RDB/rdb/build/";
static std::string table_meta_path = absolute_path + table_name + ".meta";
static std::string disk_meta_path = absolute_path + db_file_name + ".dmeta";
static std::string data_path = "/home/aaron/Course-RDB/rdb/data/supplier.tbl";
// static int32_t disk_next_page_id = -1;

void ReadTestData(std::vector<std::vector<std::string>> &records) {
    int count = 1;
    char strLine[MAX_LINE];  //读取缓冲区

    FILE *fp;
    // 判断文件是否存在及可读
    if ((fp = fopen(data_path.c_str(), "r")) == NULL) {
        printf("Open Falied!");
        return;
    }

    //循环读取每一行，直到文件尾
    while (!feof(fp)) {
        fgets(strLine, MAX_LINE, fp);  // 将fp所指向的文件一行内容读到strLine缓冲区

        char *split = strtok(strLine, "|");
        std::vector<std::string> rec;

        while (split != NULL) {
            char *word = split;
            rec.push_back(word);
            split = strtok(NULL, "|");
        }
        records.push_back(rec);

        count++;
    }
    fclose(fp);  // 关闭读文件
    return;
}

bool CreateAndInsertTable() {
    std::unique_ptr<LockManager> lock_manager = nullptr;
    std::unique_ptr<LogManager> log_manager = nullptr;
    std::unique_ptr<TransactionManager> txn_mgr =
        std::make_unique<TransactionManager>(lock_manager.get(), log_manager.get());
    Transaction *txn = txn_mgr->Begin();
    std::unique_ptr<DiskManager> disk_manager = std::make_unique<DiskManager>(db_file_name);
    // 默认是LRU,这里测试CLOCK
    std::unique_ptr<BufferPoolManager> bpm = std::make_unique<BufferPoolManager>(
        THIS_BUFFER_POOL_SIZE, disk_manager.get(), log_manager.get(), ReplacerType::CLOCK);
    std::unique_ptr<Catalog> catalog = std::make_unique<Catalog>(bpm.get(), lock_manager.get(), log_manager.get());
    std::unique_ptr<ExecutorContext> exec_ctx =
        std::make_unique<ExecutorContext>(txn, catalog.get(), bpm.get(), txn_mgr.get(), lock_manager.get());

    // Create Table which schema is for supplier.tbl
    TableGenerator gen{exec_ctx.get()};
    gen.GenerateTestTables();
    TableMetadata *table_info = exec_ctx->GetCatalog()->GetTable(table_name);

    std::vector<std::vector<std::string>> records;
    ReadTestData(records);
    for (auto rec : records) {
        std::vector<Value> val1{ValueFactory::GetIntegerValue(atoi(rec[0].c_str())),
                                ValueFactory::GetVarcharValue(rec[1]),
                                ValueFactory::GetVarcharValue(rec[2]),
                                ValueFactory::GetIntegerValue(atoi(rec[3].c_str())),
                                ValueFactory::GetVarcharValue(rec[4]),
                                ValueFactory::GetFloatValue(float(atof(rec[5].c_str()))),
                                ValueFactory::GetVarcharValue(rec[6])};

        std::vector<std::vector<Value>> raw_vals{val1};
        try {
            RID rid;
            Tuple tuple = Tuple(val1, &(table_info->schema_));
            bool is = table_info->table_->InsertTuple(tuple, &rid, exec_ctx->GetTransaction());
        } catch (TransactionAbortException &e) {
            txn_mgr->Abort(txn);
            return false;
        } catch (Exception &e) {
            return false;
        }
    }
    // 刷回脏页
    exec_ctx->GetBufferPoolManager()->FlushAllPages();

    // 保存元数据
    std::ofstream os;
    serializeTableMetadata(os, table_info);
    os.close();
    // 后续用json序列化
    // disk_next_page_id = disk_manager->next_page_id_;
    DiskManager::serializeDiskManager(os, disk_manager.get());
    // 关闭磁盘管理器
    disk_manager->ShutDown();
    LOG_INFO("CreateAndInsertTable Completed");
}

void LoadTableFromDisk() {
    std::unique_ptr<LockManager> lock_manager = nullptr;
    std::unique_ptr<LogManager> log_manager = nullptr;
    std::unique_ptr<TransactionManager> txn_mgr =
        std::make_unique<TransactionManager>(lock_manager.get(), log_manager.get());
    Transaction *txn = txn_mgr->Begin();
    std::unique_ptr<DiskManager> disk_manager = std::make_unique<DiskManager>(db_file_name);
    std::unique_ptr<BufferPoolManager> bpm = std::make_unique<BufferPoolManager>(
        THIS_BUFFER_POOL_SIZE, disk_manager.get(), log_manager.get(), ReplacerType::CLOCK);
    std::unique_ptr<Catalog> catalog = std::make_unique<Catalog>(bpm.get(), lock_manager.get(), log_manager.get());
    std::unique_ptr<ExecutorContext> exec_ctx =
        std::make_unique<ExecutorContext>(txn, catalog.get(), bpm.get(), txn_mgr.get(), lock_manager.get());

    // 恢复下一个page_id的数据
    // disk_manager->next_page_id_ = disk_next_page_id;
    // 恢复表元数据
    TableMetadata table_info_data = TableMetadata();
    TableMetadata *table_info = &table_info_data;
    std::ifstream is;
    is.open(table_meta_path);

    deserializeMetadata(is, (*table_info));
    is.close();

    is.open(disk_meta_path);
    DiskManager::deserializeDiskManager(is, *(disk_manager.get()));
    is.close();

    page_id_t table_first_page_id = table_info->table_->GetFirstPageId();

    std::cout << "First page id=" << table_first_page_id << std::endl;

    std::unique_ptr<TableHeap> th = std::move(table_info->table_);
    th->SetBufferPoolManager(bpm.get());

    RID rid;
    auto page_id = table_first_page_id;

    while (page_id != INVALID_PAGE_ID) {
        auto page = static_cast<TablePage *>(bpm->FetchPage(page_id));
        page->RLatch();
        // If this fails because there is no tuple, then RID will be the default-constructed value, which means EOF.
        auto found_tuple = page->GetFirstTupleRid(&rid);
        page->RUnlatch();
        bpm->UnpinPage(page_id, false);
        if (found_tuple) {
            break;
        }
        page_id = page->GetNextPageId();
    }

    std::unique_ptr<TableIterator> table_iter = std::make_unique<TableIterator>(th.get(), rid, nullptr);
    while (*table_iter != table_info->table_->End()) {
        Tuple raw_tuple = *(*table_iter);
        std::cout << raw_tuple.ToString(&(table_info->schema_)) << std::endl;
        ++(*table_iter);
    }
}

int main(int argc, char *argv[]) {
    CreateAndInsertTable();
    LoadTableFromDisk();
}